package com.hzit.seckill.service.impl;

import com.alibaba.fastjson2.JSON;
import com.hzit.common.constant.HzitBusinessConstants;
import com.hzit.common.utils.IdWorker;
import com.hzit.common.utils.MyDate;
import com.hzit.seckill.entity.SeckillGoods;
import com.hzit.seckill.entity.SeckillOrder;
import com.hzit.seckill.entity.SeckillStatus;
import com.hzit.seckill.interceptor.LoginInterceptor;
import com.hzit.seckill.mapper.SeckillOrderMapper;
import com.hzit.seckill.service.ISeckillOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hbxfwf
 * @since 2023-10-12
 */
@Service
public class SeckillOrderServiceImpl extends ServiceImpl<SeckillOrderMapper, SeckillOrder> implements ISeckillOrderService {
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private IdWorker idWorker;
	@Autowired
	private MultiThreadSeckillOrder multiThreadSeckillOrder;        //异步下单类
	@Autowired
	private RedissonClient redissonClient;
	//1. 下单前的准备操作
	@Override
	public void saveOrder(String id) {
		//1.1 避免重复排队
		Long rs = redisTemplate.opsForValue().increment(HzitBusinessConstants.SECKILL_REPEAT_ORDER, 1);
		//1.1.1 判断值是否大于1，如果大于，证明曾经排过队
		if(rs > 1){
			throw new RuntimeException("不能重复排队!");
		}
		//1.2 为了排队的公平性，可以将当前用户状态值以list的方式左边放到redis中，
		//1.2.1 生成订单id
		long orderId = idWorker.nextId();
		//1.2.2 构造状态对象
		String name = LoginInterceptor.threadLocal.get().getLogin();
		Date date = MyDate.getDateMenus().get(0);
		String time = MyDate.getDateStr(date);
		//1.2.3 从redis中根据商品id查询商品
		String s = (String) redisTemplate.opsForHash().get(HzitBusinessConstants.SECKILL_GOODS_PREFIX + time, id);
		SeckillGoods seckillGoods = JSON.parseObject(s, SeckillGoods.class);
		float money = seckillGoods.getCostPrice().floatValue();
		//1.2.4 构造Seckillstatus对象
		SeckillStatus seckillStatus = new SeckillStatus(name,new Date(),1,Long.parseLong(id),money,orderId,time);
		//1.2.5 将状态对象放到redis中,正式下单后从右边弹出用户
		redisTemplate.boundListOps(HzitBusinessConstants.SECKILL_ORDER_QUEUE).leftPush(JSON.toJSONString(seckillStatus));

		//1.3 因为后面弹出这个状态值后，就没有了用户信息，所以我们可以将此用户状态值在redis中备份一份
		redisTemplate.opsForValue().set(HzitBusinessConstants.SECKILL_ORDER_QUEUE_BACK,JSON.toJSONString(seckillStatus));

		//1.4 使用信号量限流
		RSemaphore semaphore = redissonClient.getSemaphore(HzitBusinessConstants.SECKILL_LIMIT_FLOW_PREFIX + id);
		//1.5 以商品存库为值放到信号量中
		semaphore.trySetPermits(seckillGoods.getStockCount());

		// 开始异步下单
		multiThreadSeckillOrder.fareOrder(id);


	}

}
